/*
 *  token.c
 *  Fib
 *
 *  Created by John Bassham on 11/13/08.
 *  Copyright 2008 __MyCompanyName__. All rights reserved.
 *
 */

#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <assert.h>

#include "bool.h"

#include "token.h"

#define TOKENIZER_BUFSZ			1024
#define TOKENIZER_TOKENS_BUFSZ		255

static void inittokenerrors(struct tokenizer *t)
{
	unsigned char offsetbuf[sizeof(struct tokenizer)];
	unsigned char *offset = offsetbuf + offsetof(struct tokenizer, errors);
	*((enum token_error *) (offset + offsetof(struct token_errors_struct, null_token))) = TOKENERROR_NULL_TOKEN;
	*((enum token_error *) (offset + offsetof(struct token_errors_struct, malformed_indentation))) = TOKENERROR_MALFORMED_INDENTATION;
	*((enum token_error *) (offset + offsetof(struct token_errors_struct, unrecognized_oper))) = TOKENERROR_UNRECOGNIZED_OPER;
	memcpy(t, offsetbuf, sizeof(struct tokenizer));
}

bool init_tokenizer(struct tokenizer *t, FILE *f)
{
	bool b;
	
	assert(t);
	assert(f);
	
	memset(t, 0, sizeof(struct tokenizer));
	inittokenerrors(t);
			
	t->bufsz = TOKENIZER_BUFSZ;
	t->line_tokens_bufsz = TOKENIZER_TOKENS_BUFSZ;
	t->buf = (char *) malloc(sizeof(char) * t->bufsz);
	t->line_tokens = (struct token *) malloc(sizeof(struct token) * t->line_tokens_bufsz);
	if (t->buf && t->line_tokens) {
		t->line_tokens_cnt = 0;
		t->iter_indx = 0;
		t->reached_eof = false;
		t->file = f;
		memset(t->buf, NULL, t->bufsz * sizeof(char));
		memset(t->line_tokens, NULL, t->line_tokens_bufsz * sizeof(struct token));
		b = true;
	} else {
		b = false;
		printf("malloc failure in %s, line %u\n", __FILE__, __LINE__);
	}
	return b;
}

static INLINE void resetcbuf(char *buf, const size_t sz)
{
	int i;
	for (i = 0; i < sz && buf[i]; i++)
		buf[i] = '\0';
}

static INLINE void resettokenbuf(struct token *tokens, const size_t sz)
{
	int i;
	for (i = 0; i < sz; i++)
		memset(tokens + i, 0, sizeof(struct token));
}

static INLINE bool tokenizeline(struct token **tokbuf, int *bufcnt, const int tokbufsz, const char *line)
{
	assert(tokbuf && bufcnt && line);
	
	resettokenbuf(*tokbuf, tokbufsz);
}

const struct token next_token(struct tokenizer *t)
{
	struct token token;
	assert(t);
	if (t->line_tokens) {
		if (t->iter_indx < t->line_tokens_cnt)
			token = t->line_tokens[t->iter_indx++];
		else {
			size_t len;
			char *readptr;
			char *const buf = t->buf;
			const size_t bufsz = t->bufsz;
			resetcbuf(buf, bufsz);
			readptr = fgets(buf, bufsz, t->file);
			if (readptr != buf)
				if (feof(t->file))
					t->reached_eof = true;
			len = strlen(buf);
			if (!tokenizeline(&t->line_tokens, &t->line_tokens_cnt, t->line_tokens_bufsz, buf)) {
				t->line_tokens = NULL;
			}
		}
	} else {
		token.type = TOKENTYPE_ERROR;
		token.value = &t->errors.null_token;
	}
	return token;
}
